-- HeroDisplay
-- Created by chengb Mar/2/2015
-- 负责冈布奥外观显示

require "game/ui/module/hero/HeroAnimation"

-- HeroDisplay继承自Node
HeroDisplay = class("HeroDisplay", function()
    return cc.Node:create();
end);

function HeroDisplay.create(modelId, extra)
    return HeroDisplay.new(modelId, extra);
end

-- 构造函数
function HeroDisplay:ctor(modelId, extra)
    self:setName("HeroDisplay");

    self.modelId = modelId;

    -- 创建动画
    local animName = PetM.queryModel(self.modelId, "animation");
    if extra and extra["anim_name"] then
        animName = extra["anim_name"];
    end
    local animation = HeroAnimation.create("slime_animation/" .. animName .. ".csb");
    self.animation = animation;
    self:addChild(animation);

    -- 替换皮肤
    self:applySkin();

    -- 获取结束帧
    self.endFrameIndex = PetM.queryModel(self.modelId, "end_frame_index");

    -- 默认播放待机动作
    self:gotoIdle();

    self:registerScriptHandler(function(ev)
        if ev == "exitTransitionStart" then
            if self.animation == nil then
                return;
            end

            if self.animation.action then
                self.animation.action:clearFrameEventCallFunc();
            end

            self.animation:destroy();
        end
    end);
end

-- 替换皮肤
function HeroDisplay:applySkin()
    local skinId = tonumber(PetM.queryModel(self.modelId, "skin"));
    if skinId == nil or skinId == 0 then
        return;
    end

    local skinPath = PetM.querySkin(skinId, "path");
    local skinParts = PetM.querySkin(skinId, "parts");

    local node = self.animation.node;

    -- 替换单个部件
    local function applyPart(part, fullPath)
        local children = node:getChildren();
        for i = 1, #children do
            local child = children[i];
            if child:getName() == part then
                local body = findChildByName(child, "SpriteObject");
                body:setTexture(fullPath);
            end
        end
    end

    -- 遍历零部件
    for i = 1, #skinParts do
        local part = skinParts[i]..".png";
        local fullPath = "images/slime_skin/"..skinPath.."/"..part;
        applyPart(part, fullPath);
    end
end

-- 切换冈布奥动作
function HeroDisplay:switchAction(actionType, isPlayAudio)
    if self.animation == nil then
        return;
    end

    -- 待机
    if HERO_ACTION_IDLE == actionType then
        self:gotoIdle();
    -- 受创
    elseif HERO_ACTION_DAMAGED == actionType then
        self:gotoDamaged();
    -- 攻击
    elseif HERO_ACTION_ATTACK == actionType then
        self:gotoAttack();
    -- 对自身施法
    elseif HERO_ACTION_CAST_ME == actionType then
        self:gotoCastMe();
    -- 对他人施法
    elseif HERO_ACTION_CAST_OTHER == actionType then
        self:gotoCastOther();
    -- 说话
    elseif HERO_ACTION_ATTACK == actionType then
        self:gotoTalk();
    else
        -- 暂不支持
        cclog("### 无法切换冈布奥动作，未知的冈布奥动作类型：" .. tostring(actionType));
    end

    -- 默认不播放音效
    isPlayAudio = isPlayAudio or false;

    if isPlayAudio then
        self:playAudio(actionType);
    end
end

-- 播放待机动作
function HeroDisplay:gotoIdle()
    -- 切换状态
    self.actionType = HERO_ACTION_IDLE;

    -- 获取帧范围和播放速度
    local frameRange = PetM.queryModel(self.modelId, "idle_frame");
    local speed = PetM.queryModel(self.modelId, "idle_speed");

    -- 播放
    self.animation:play(HERO_ACTION_IDLE, true, frameRange[1], frameRange[2], speed);

    -- 需要暂停
    if self.needPause then
        self.animation:pause();
    end
end

-- 播放受创动作
function HeroDisplay:gotoDamaged()
    -- 切换状态
    self.actionType = HERO_ACTION_DAMAGED;

    -- 获取帧范围和播放速度
    local frameRange = PetM.queryModel(self.modelId, "damaged_frame");
    local speed = PetM.queryModel(self.modelId, "damaged_speed");

    -- 播放
    self.animation:play(HERO_ACTION_DAMAGED, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    self:registerFrameEvent();
end

-- 播放攻击动作
function HeroDisplay:gotoAttack()
    -- 切换状态
    self.actionType = HERO_ACTION_ATTACK;

    -- 获取帧范围和播放速度
    local frameRange = PetM.queryModel(self.modelId, "attack_frame");
    local speed = PetM.queryModel(self.modelId, "attack_speed");

    -- 播放
    self.animation:play(HERO_ACTION_ATTACK, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    self:registerFrameEvent();
end

-- 播放对自身施法动作
function HeroDisplay:gotoCastMe()
    -- 切换状态
    self.actionType = HERO_ACTION_CAST_ME;

    -- 获取帧范围和播放速度
    local frameRange = PetM.queryModel(self.modelId, "cast_me_frame");
    local speed = PetM.queryModel(self.modelId, "cast_me_speed");

    -- 播放
    self.animation:play(HERO_ACTION_CAST_ME, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    self:registerFrameEvent();
end

-- 播放对他人施法动作
function HeroDisplay:gotoCastOther()
    -- 切换状态
    self.actionType = HERO_ACTION_CAST_OTHER;

    -- 获取帧范围和播放速度
    local frameRange = PetM.queryModel(self.modelId, "cast_other_frame");
    local speed = PetM.queryModel(self.modelId, "cast_other_speed");

    -- 播放
    self.animation:play(HERO_ACTION_CAST_OTHER, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    self:registerFrameEvent();
end

-- 播放说话动作
function HeroDisplay:gotoTalk()
    -- 切换状态
    self.actionType = HERO_ACTION_SPEAK;

    -- 获取帧范围和播放速度
    local frameRange = PetM.queryModel(self.modelId, "speak_frame");
    local speed = PetM.queryModel(self.modelId, "speak_speed");

    -- 播放
    self.animation:play(HERO_ACTION_SPEAK, false, frameRange[1], frameRange[2], speed);

    -- 注册帧回调函数
    self:registerFrameEvent();
end

-- 注册帧回调函数
function HeroDisplay:registerFrameEvent()
    -- 移除帧回调处理函数
    self.animation.action:clearFrameEventCallFunc();

    -- 先将self保存起来，避免在帧回调的过程中self被释放
    local this = self;

    -- 帧回调处理函数
    local function onFrameEvent(frame)
        if nil == frame then
            return;
        end

        -- 使用pilor帧，确保每帧都会有回调过来
        if frame.getEvent and frame:getEvent() == "pilor" then
            -- 帧索引的下标是从0开始的，而配置的下标是从1开始的，故这里要加1
            local frameIndex = frame:getFrameIndex() + 1;

            -- 当前帧没有播放完毕
            if this.isCurrentActionOver and not this:isCurrentActionOver(frameIndex) then
                return;
            end

            -- 移除帧回调处理函数
            if not this.animation then
                return;
            end

            this.animation.action:clearFrameEventCallFunc();

            if this.actionType == HERO_ACTION_IDLE then
                -- 如果是待机结束，无需处理（待机是循环播放的）
                return;
            end

            local function callback()
                if not this.animation then
                    return;
                end
                -- 切换至待机状态
                this:gotoIdle();
            end

            performWithDelay(this.animation, callback, 0.1);
        end
    end

    -- 设置帧回调处理函数
    self.animation.action:setFrameEventCallFunc(onFrameEvent);
end

---------------------------
-- 获取帧范围
-- @param actionType 动作类型
function HeroDisplay:getFrameRange(actionType)
    local range;
    if actionType == HERO_ACTION_IDLE then
        range = PetM.queryModel(self.modelId, "idle_frame");
    elseif actionType == HERO_ACTION_DAMAGED then
        range =  PetM.queryModel(self.modelId, "damaged_frame");
    elseif actionType == HERO_ACTION_ATTACK then
        range =  PetM.queryModel(self.modelId, "attack_frame");
    elseif actionType == HERO_ACTION_CAST_ME then
        range =  PetM.queryModel(self.modelId, "cast_me_frame");
    elseif actionType == HERO_ACTION_CAST_OTHER then
        range =  PetM.queryModel(self.modelId, "cast_other_frame");
    elseif actionType == HERO_ACTION_SPEAK then
        range =  PetM.queryModel(self.modelId, "speak_frame");
    else
        trace("HeroDisplay", "无法获取帧范围，未知的动作类型：" .. tostring(actionType));
        return nil;
    end

    -- 检查参数合法性
    if self.animation then
        local maxIndex = self.animation.action:getDuration();
        if range[2] > maxIndex then
            range[2] = maxIndex;
        end
    end

    return range;
end

----------------------------
-- 判断当前动作是否已经播完
-- @param curFrameIndex   播放到第几帧
function HeroDisplay:isCurrentActionOver(curFrameIndex)
    local curActionType = self.actionType;
    local endIndex = PetM.queryModel(self.modelId, "end_frame_index");
    if curFrameIndex >= endIndex then
        -- 已经到最后一帧了
        return true;
    end

    local range = self:getFrameRange(curActionType);
    if not range then
        return false;
    end

    if curFrameIndex >= range[1] and curFrameIndex < range[2] then
        -- 还没播完
        return false;
    end

    return true;
end

-- 播放音效
function HeroDisplay:playAudio(actionType)
    local audio;
    if actionType == HERO_ACTION_CAST_ME or actionType == HERO_ACTION_CAST_OTHER then
        -- 施法
        audio = PetM.queryModel(self.modelId, "cast_audio");
    elseif actionType == HERO_ACTION_ATTACK then
        -- 攻击
        audio = PetM.queryModel(self.modelId, "attack_audio");
    elseif actionType == HERO_ACTION_DAMAGED then
        -- 受创
        audio = PetM.queryModel(self.modelId, "damaged_audio");
    end

    if type(audio) == 'string' and audio ~= "" then
        AudioM.playFx(audio);
    end
end

-- 移除动画
function HeroDisplay:removeNormalAnimation()
    if self.animation == nil then
        return;
    end

    self.animation:pause();
    self.animation:destroy();
    self.animation = nil;
end

-- 设置是否需要暂停
function HeroDisplay:setNeedPause(needPause)
    --self.needPause = needPause;
end

-- 暂停所有动画
function HeroDisplay:pauseActions()
    if self.animation == nil then
        return;
    end

    -- 设置暂停
    self:setNeedPause(true);
    self:gotoIdle();
end


